import bpy
import os
import json
from os.path import join, exists, normcase
from typing import Union, List
from ...addon.naming import FluidLabNaming
from ...addon.paths import FluidLabPaths
from ..functions.object import enter_object_mode
from bpy.types import Context, Collection, Object, Material
from ..functions.collections import create_new_collection, set_active_collection_by_name


def append_from_library(
        context,
        fpath: str,
        inner_path: str,
        blend_file: str,
        item_name: str,
        dest_collection: Collection = None,
        only_if_not_exist: bool = True) -> Union[None, List[Object]]:

    ############################################
    ''' 
        Some Info:
        inner_paths:  "Object" or  "NodeTree" etc 
        item_name: the Obj name or GN node name
    '''
    ############################################

    enter_object_mode(context)

    if inner_path == "Object":
        previous_sel = context.selected_objects

    # addon_folder = VDBLabPaths.ROOT
    # fpath = join(addon_folder, 'libs', 'blends')

    def append(fpath, blend_file, inner_path, item_name):

        if dest_collection:
            create_new_collection(context, dest_collection)
            set_active_collection_by_name(context, dest_collection)
            context.collection[FluidLabPaths.ROOT_BASENAME] = True

        # print(fpath, blend_file, inner_path, item_name)

        bpy.ops.wm.append(
            filename=item_name,
            filepath=join(fpath, blend_file, inner_path, item_name),
            directory=join(fpath, blend_file, inner_path),
        )

    if inner_path == "Object":
        item_exit = item_name in bpy.data.objects
    elif inner_path == "NodeTree":
        item_exit = item_name in bpy.data.node_groups
    elif inner_path == "Material":
        item_exit = item_name in bpy.data.materials
    elif inner_path == "World":
        item_exit = item_name in bpy.data.worlds
    elif inner_path == "Collection":
        item_exit = item_name in bpy.data.collections

    # print(fpath, blend_file, inner_path, item_name)

    if only_if_not_exist:
        if not item_exit:
            append(fpath, blend_file, inner_path, item_name)
    else:
        append(fpath, blend_file, inner_path, item_name)

    if inner_path == "Object":
        return [obj for obj in context.selected_objects if obj not in previous_sel]

    if inner_path == "Collection" and dest_collection is not None:
        return bpy.data.collections[dest_collection]


def append_domain_material(blend_file: str, target_lib: str, new_name: str) -> Material:
    addon_folder = FluidLabPaths.ROOT
    fpath = join(addon_folder, 'libs', 'blends', blend_file)

    with bpy.data.libraries.load(fpath, link=False) as (data_src, data_dst):
        data_dst.materials = [target_lib]

    mat = data_dst.materials[0]
    if mat:
        mat.name = new_name
        mat[FluidLabNaming.FLUIDLAB_MAT] = True
        return mat


def read_json_file(file_name: str) -> json:
    addon_folder = FluidLabPaths.ROOT
    current_dir = os.getcwd()
    os.chdir(addon_folder)

    with open(file_name, "r") as jsonFile:
        data = json.load(jsonFile)

    os.chdir(current_dir)

    return data


def write_json_file(data: json, file_name: str) -> None:
    json_string = json.dumps(data, sort_keys=False, indent=4)

    with open(file_name, 'w') as outfile:
        outfile.write(json_string)


def read_fluid_preset(context:Context, filename:str, file:str, target:str = "properties") -> Union[None, dict]:
    
    if not exists(file):
        print(f"{file} not exist!!")
        return None
    
    wm = context.window_manager

    if filename in wm:
        preset_data = json.loads(wm[filename])
    else:

        print(f"[read_fluid_preset] Read from disk {filename}!")
        
        with open(file, 'r') as file:
            preset_data = json.load(file)        

        wm[filename] = json.dumps(preset_data, indent=4, default=str)

    if target in preset_data:
        return preset_data[target]
    else:
        return preset_data